import 'dart:math';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_rating_bar/flutter_rating_bar.dart';

enum FoldingState {
  open,
  close,
}

class FoldingCell extends StatefulWidget {
  /// 当卡片打开或者关闭时的回调
  final ValueChanged<FoldingState> onChanged;

  /// 卡片的预设状态
  final FoldingState foldingState;

  const FoldingCell({
    Key? key,
    required this.onChanged,
    this.foldingState = FoldingState.close,
  }) : super(key: key);

  @override
  State<FoldingCell> createState() => _FoldingCellState();
}

class _FoldingCellState extends State<FoldingCell>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      vsync: this,
      duration: Duration(milliseconds: 360),
    );

    if (widget.foldingState == FoldingState.close) {
      _controller.forward(from: 1.0);
    } else {
      _controller.reverse(from: 0);
    }

    _controller.addStatusListener((status) {
      if (status == AnimationStatus.forward) {
        /// 执行了关闭动画
        widget.onChanged(FoldingState.close);
      }
      if (status == AnimationStatus.reverse) {
        /// 执行了打开动画
        widget.onChanged(FoldingState.open);
      }
    });
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  /// 折叠或打开
  void toggle() {
    if (_controller.value == 1) {
      _controller.reverse();
    } else {
      _controller.forward();
    }
  }

  /// 根据开始、结束角度 + 动画过程间隔生成 Animation，
  Animation<double> generateAnimation({
    /// 旋转的开始角度
    beginAngle = double,

    /// 旋转的结束角度
    endAngle = double,

    /// 该段动画起点
    intervalBegin = double,

    /// 该段动画结束点
    intervalEnd = double,
  }) {
    return Tween<double>(begin: beginAngle, end: endAngle).animate(
      CurvedAnimation(
        parent: _controller,
        curve: Interval(intervalBegin, intervalEnd),
      ),
    );
  }

  @override
  Widget build(BuildContext context) {
    double padding = 8.0;
    double cardHeight = 165.0;
    // double totalHeight = 501.0;
    double totalHeight = 553.0;
    return GestureDetector(
      onTap: () {
        toggle();
      },

      /// 用 AnimatedBuilder 方便在 listView 中动态改变高度时做动画
      child: AnimatedBuilder(
        animation: _controller,
        builder: (BuildContext context, Widget? child) {
          return Container(
            padding: EdgeInsets.symmetric(
              horizontal: padding * 2,
              vertical: padding,
            ),

            /// 这里计算高度的原理是，开始时只显示卡片的一部分，
            /// 后面随着动画过程增加 Cell 高度
            height: cardHeight +
                padding * 2 +
                (totalHeight - cardHeight) * (1.0 - _controller.value),
            child: Stack(
              fit: StackFit.expand,
              children: [
                Positioned(
                  top: 0.0,
                  left: 0.0,
                  right: 0.0,
                  child: Column(
                    children: [
                      Container(
                        child: Stack(
                          children: [
                            ClipRRect(
                              borderRadius: BorderRadius.vertical(
                                top: Radius.circular(padding),

                                /// 底部的圆角随动画改变
                                bottom: Radius.circular(8 * _controller.value),
                              ),
                              child: Container(
                                height: cardHeight,
                                child: component2(),
                              ),
                            ),
                            Container(
                              height: cardHeight,
                              child: FoldingComponent(
                                backChild: component1(),
                                animation: generateAnimation(
                                  beginAngle: 90.0,
                                  endAngle: 0.0,
                                  intervalBegin: 0.75,
                                  intervalEnd: 1.0,
                                ),
                              ),
                            )
                          ],
                        ),
                      ),
                      Container(
                        height: 96,
                        child: FoldingComponent(
                          frontChild: component3(),
                          animation: generateAnimation(
                            beginAngle: 0.0,
                            endAngle: 90.0,
                            intervalBegin: 0.5,
                            intervalEnd: 0.75,
                          ),
                        ),
                      ),
                      Container(
                        height: 100,
                        child: FoldingComponent(
                          isFrontShowing: true,
                          frontChild: component4(),
                          animation: generateAnimation(
                            beginAngle: 0.0,
                            endAngle: 180.0,
                            intervalBegin: 0.3,
                            intervalEnd: 0.5,
                          ),
                        ),
                      ),
                      Container(
                        height: 104,
                        child: FoldingComponent(
                          isFrontShowing: true,
                          frontChild: component5(),
                          animation: generateAnimation(
                            beginAngle: 0.0,
                            endAngle: 180.0,
                            intervalBegin: 0.2,
                            intervalEnd: 0.3,
                          ),
                        ),
                      ),
                      Container(
                        height: 88,
                        child: FoldingComponent(
                          isFrontShowing: true,
                          frontChild: component6(),
                          animation: generateAnimation(
                            beginAngle: 0.0,
                            endAngle: 180.0,
                            intervalBegin: 0.0,
                            intervalEnd: 0.2,
                          ),
                        ),
                      ),
                    ],
                  ),
                ),
              ],
            ),
          );
        },
      ),
    );
  }
}

class FoldingComponent extends AnimatedWidget {
  final Animation<double> animation;

  /// 每个组件有正反两面 widget
  /// 这是正面的 widget
  final Widget? frontChild;

  /// 背面的 widget
  final Widget? backChild;

  /// 是否在展示正面的 widget
  final bool isFrontShowing;

  /// 180 度
  final double anglePI = 180.0;

  /// 90 度
  final double angleHalfPI = 90.0;

  /// 是否有背面的 widget
  bool get hasBackChild => backChild != null;

  const FoldingComponent({
    Key? key,
    required this.animation,
    this.frontChild,
    this.isFrontShowing = false,
    this.backChild,
  }) : super(key: key, listenable: animation);

  @override
  Widget build(BuildContext context) {
    /// 对齐方式
    Alignment alignment =
        hasBackChild ? Alignment.bottomCenter : Alignment.topCenter;

    /// 旋转角度
    double rotateAngle =
        (pi / anglePI) * (hasBackChild ? animation.value : -animation.value);

    /// 视图底部的圆角
    BorderRadius bottomRadius = BorderRadius.vertical(
      bottom: Radius.circular(
        8 * ((animation.value == 0) ? 0 : 1),
      ),
    );

    /// 背景颜色
    Color backgroundColor = (isFrontShowing && animation.value < angleHalfPI) ||
            (!isFrontShowing && animation.value == 0.0)
        ? Colors.transparent
        : Colors.white.withOpacity(0.8);

    /// 当前需要展示的部件
    Widget? showingChild = (isFrontShowing && animation.value < angleHalfPI) ||
            (animation.value == 0.0 && !hasBackChild)
        ? frontChild
        : backChild;

    return animation.value == anglePI && !hasBackChild

        /// 翻转到背面，但是不存在背面 Widget
        ? SizedBox.shrink()
        : Transform(
            alignment: alignment,
            transform: Matrix4.identity()

              /// 3D动画矩阵设计
              ..setEntry(3, 2, 0.001)

              /// 沿着 X 轴做旋转动画
              ..rotateX(rotateAngle),
            child: Container(
              color: backgroundColor,
              child: showingChild,
            ),
          );
  }
}

/// 组件 1
Widget component1() {
  double height = 165.0;
  return ClipRRect(
    borderRadius: BorderRadius.all(Radius.circular(8.0)),
    child: Container(
      height: height,
      child: Row(
        children: [
          Container(
            width: 88,
            color: HexColor.fromHex('5D4A99'),
            child: Column(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: [
                Text(
                  '\$25',
                  textAlign: TextAlign.center,
                  style: TextStyle(
                    fontSize: 24,
                    color: Colors.white,
                  ),
                ),
                Text.rich(TextSpan(style: TextStyle(), children: [
                  TextSpan(
                    text: 'TODAY\n',
                    style: TextStyle(
                      color: Colors.grey,
                      fontSize: 10,
                    ),
                  ),
                  TextSpan(
                    text: '06:30 PM',
                    style: TextStyle(
                      color: Colors.white,
                      fontSize: 13,
                    ),
                  )
                ]))
              ],
            ),
          ),
          Expanded(
            child: Container(
              height: double.infinity,
              color: Colors.white,
              child: Column(
                mainAxisAlignment: MainAxisAlignment.spaceAround,
                children: [
                  Row(
                    mainAxisAlignment: MainAxisAlignment.spaceAround,
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      SizedBox(width: 10),
                      Image.asset(
                        'assets/images/folding_cell/dots.png',
                        width: 16,
                        height: 42,
                      ),
                      SizedBox(
                        width: 6,
                      ),
                      Expanded(
                        child: Column(
                          crossAxisAlignment: CrossAxisAlignment.start,
                          mainAxisSize: MainAxisSize.min,
                          children: [
                            Text(
                              'W 90th St, New York , 10024',
                              style: TextStyle(
                                fontWeight: FontWeight.w500,
                                fontSize: 16,
                              ),
                            ),
                            Container(
                              margin: EdgeInsets.fromLTRB(0, 8, 10, 8),
                              width: double.infinity,
                              height: 0.5,
                              color: Colors.grey,
                            ),
                            Text(
                              '46th Ave, Woodside, 10011',
                              style: TextStyle(
                                fontWeight: FontWeight.w500,
                                fontSize: 16,
                              ),
                            ),
                          ],
                        ),
                      ),
                    ],
                  ),
                  Row(
                    mainAxisAlignment: MainAxisAlignment.spaceAround,
                    children: [
                      explainText('REQUEST', '6'),
                      explainText('PLEDGE', '\$150'),
                      explainText('WEIGHT', 'light'),
                    ],
                  ),
                ],
              ),
            ),
          )
        ],
      ),
    ),
  );
}

/// 组件 2
Widget component2() {
  return Container(
    child: Column(
      children: [
        Container(
          padding: EdgeInsets.symmetric(horizontal: 10),
          height: 44,
          color: HexColor.fromHex('5D4A99'),
          child: Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              Icon(
                Icons.menu,
                color: Colors.white,
              ),
              Text(
                '# 2618-3157',
                style: TextStyle(
                  fontSize: 16,
                  color: Colors.white,
                ),
              ),
              Text(
                '\$25',
                style: TextStyle(
                  fontSize: 16,
                  color: Colors.white,
                ),
              ),
            ],
          ),
        ),
        Stack(
          children: [
            Image.asset(
              'assets/images/folding_cell/image.png',
              width: double.infinity,
              height: 121,
              fit: BoxFit.cover,
            ),
            Positioned.fill(
              bottom: 12,
              child: Row(
                crossAxisAlignment: CrossAxisAlignment.end,
                mainAxisSize: MainAxisSize.max,
                mainAxisAlignment: MainAxisAlignment.spaceAround,
                children: [
                  explainText('REQUEST', '6', subtitleColor: Colors.white),
                  explainText('PLEDGE', '\$150', subtitleColor: Colors.white),
                  explainText('WEIGHT', 'light', subtitleColor: Colors.white),
                ],
              ),
            )
          ],
        ),
      ],
    ),
  );
}

/// 组件 3
Widget component3() {
  return Container(
    color: Colors.white,
    padding: EdgeInsets.fromLTRB(10, 10, 10, 0),
    child: Column(
      children: [
        Padding(
          padding: EdgeInsets.only(bottom: 6),
          child: Text(
            'SENDER',
            style: TextStyle(
              color: Colors.grey,
              fontSize: 12,
            ),
          ),
        ),
        Row(
          children: [
            ClipRRect(
              borderRadius: BorderRadius.circular(8.0),
              child: Image.asset(
                'assets/images/folding_cell/avatar.png',
                width: 48,
                height: 48,
              ),
            ),
            SizedBox(
              width: 10,
            ),
            Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  'Edward Norton',
                  style: TextStyle(
                    fontWeight: FontWeight.bold,
                    fontSize: 16,
                    color: Colors.black87,
                  ),
                ),
                SizedBox(
                  height: 4,
                ),
                Row(
                  children: [
                    RatingBar.builder(
                      initialRating: 3,
                      minRating: 1,
                      itemSize: 14,
                      direction: Axis.horizontal,
                      allowHalfRating: true,
                      itemPadding: EdgeInsets.symmetric(horizontal: 0),
                      itemBuilder: (ctx, _) => Icon(
                        Icons.star,
                        color: Colors.amber,
                      ),
                      onRatingUpdate: (double value) {
                        if (kDebugMode) {
                          print(value);
                        }
                      },
                    ),
                    SizedBox(
                      width: 4,
                    ),
                    Text(
                      '(26)',
                      style: TextStyle(
                        color: Colors.grey,
                        fontSize: 12,
                      ),
                    ),
                  ],
                ),
              ],
            ),
            Spacer(),
            Icon(
              Icons.arrow_forward_ios,
              size: 16,
              color: Colors.grey,
            ),
          ],
        ),
        Divider(
          color: Colors.grey[300],
        ),
      ],
    ),
  );
}

/// 组件 4
Widget component4() {
  return Container(
    color: Colors.white,
    padding: EdgeInsets.all(10),
    child: Row(
      children: [
        Expanded(
            child:
                multipleLineText('FROM', 'W 90th St', 'New York, NY' '10025')),
        Expanded(
          child: multipleLineText('T0', '46th Ave', 'Woodside, NY 11101'),
        ),
        Icon(
          Icons.arrow_forward_ios,
          size: 16,
          color: Colors.grey,
        )
      ],
    ),
  );
}

/// 组件 5
Widget component5() {
  return Container(
    color: Colors.white,
    padding: EdgeInsets.symmetric(vertical: 10, horizontal: 10),
    child: Row(
      children: [
        Expanded(
            child: multipleLineText(
                'DELIVERY',
                '06:30 pm',
                'May 16, '
                    '2021')),
        Expanded(
          child: multipleLineText('REQUEST DEADLINE', '24 minutes', ''),
        ),
        Icon(
          Icons.arrow_forward_ios,
          size: 16,
          color: Colors.grey,
        )
      ],
    ),
  );
}

/// 组件 6
Widget component6() {
  return ClipRRect(
    borderRadius: BorderRadius.vertical(bottom: Radius.circular(8)),
    child: Container(
      color: Colors.white,
      padding: EdgeInsets.all(10),
      child: Column(
        children: [
          ElevatedButton(
            style: ElevatedButton.styleFrom(
              shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(12),
              ),
              elevation: 0,
              primary: HexColor.fromHex('FEBE16'),
            ),
            onPressed: () {},
            child: Container(
              height: 36,
              child: Center(
                child: Text(
                  'SENDER REQUEST',
                  style: TextStyle(
                      fontSize: 15,
                      color: Colors.black87,
                      fontWeight: FontWeight.bold),
                ),
              ),
            ),
          ),
          SizedBox(
            height: 4,
          ),
          Text(
            '5 people have sent a request',
            style: TextStyle(
              color: Colors.grey,
              fontSize: 12,
              fontWeight: FontWeight.w500,
            ),
          )
        ],
      ),
    ),
  );
}

/// 三行文字
Widget multipleLineText(String line1, String line2, String line3) {
  return Text.rich(
    TextSpan(
      style: TextStyle(height: 1.4),
      children: [
        TextSpan(
          text: '$line1\n',
          style: TextStyle(
            color: Colors.grey,
            fontSize: 12,
          ),
        ),
        TextSpan(
          text: '$line2\n',
          style: TextStyle(
            color: Colors.black87,
            fontWeight: FontWeight.bold,
            fontSize: 14,
          ),
        ),
        TextSpan(
          text: line3,
          style: TextStyle(
            color: Colors.black87,
            fontSize: 12,
          ),
        ),
      ],
    ),
  );
}

///
Widget explainText(String title, String subtitle,
    {Color? titleColor, Color? subtitleColor}) {
  return Text.rich(
    TextSpan(
        style: TextStyle(
          height: 1.4,
        ),
        children: [
          TextSpan(
            text: '$title\n',
            style: TextStyle(
              color: titleColor ?? Colors.grey,
              fontSize: 13,
            ),
          ),
          TextSpan(
            text: subtitle,
            style: TextStyle(
              color: subtitleColor ?? Colors.blueGrey,
              fontWeight: FontWeight.bold,
              fontSize: 17,
            ),
          ),
        ]),
  );
}

/// utils
extension HexColor on Color {
  /// String is in the format "aabbcc" or "ffaabbcc" with an optional leading "#".
  static Color fromHex(String hexString) {
    final buffer = StringBuffer();
    if (hexString.length == 6 || hexString.length == 7) buffer.write('ff');
    buffer.write(hexString.replaceFirst('#', ''));
    return Color(int.parse(buffer.toString(), radix: 16));
  }

  /// Prefixes a hash sign if [leadingHashSign] is set to `true` (default is `true`).
  String toHex({bool leadingHashSign = true}) => '${leadingHashSign ? '#' : ''}'
      '${alpha.toRadixString(16).padLeft(2, '0')}'
      '${red.toRadixString(16).padLeft(2, '0')}'
      '${green.toRadixString(16).padLeft(2, '0')}'
      '${blue.toRadixString(16).padLeft(2, '0')}';
}
